
#include <string>
#include <iostream>
#include <algorithm>
#include "Packet.hpp"
#include "SocketHelper.hpp"

//#pragma warning(disable : 4267)

namespace cybertron {

NetPacket::NetPacket() {
#ifdef PACKET_MEMORY_PREALLOCATE
	_head = (uint8_t *)malloc(PACKET_MAX_SIZE);
	_body = _head+PACKET_HEAD_LENGTH;
#else
	_body = nullptr;
#endif
	_head_pos = _body_len = _body_pos = 0;
	memset(_head, 0, PACKET_HEAD_LENGTH);
}

NetPacket::~NetPacket() {
#ifdef PACKET_MEMORY_PREALLOCATE
	free(_head);
#else
	if (_body)
		free(_body);
#endif
}

int NetPacket::set_data(const void * data, size_t len)
{
	if (len > (PACKET_MAX_SIZE-PACKET_HEAD_LENGTH)) {
		std::cout << "data size is too large" << "\n";
		return -1;
	}
#ifdef PACKET_MEMORY_PREALLOCATE
#else
	_body = (uint8_t *)malloc(len);
#endif
	memcpy(_body, data, len);
	_body_len = len;

	uint16_t l = static_cast<uint16_t>(len);
	uint8_t *p = (uint8_t *)_head;
	SocketHelper::net_htons(l, p);

	return 0;
}

size_t NetPacket::appand_data(const void * data, size_t len)
{
#ifdef PACKET_MEMORY_PREALLOCATE
	if (_head_pos < PACKET_HEAD_LENGTH) {
		if (_head_pos + len >= PACKET_HEAD_LENGTH) {
			size_t head_part = PACKET_HEAD_LENGTH - _head_pos;
			size_t body_part = len - head_part;

			memcpy(_head + _head_pos, data, head_part);
			_head_pos = PACKET_HEAD_LENGTH;
			_body_len = SocketHelper::net_ntohs(_head);
			//std::cout << "message length: " << _body_len << "\n";
			if (_body_len > PACKET_MAX_SIZE - PACKET_HEAD_LENGTH) {
				std::cout << "invalid packet\n";
				return 0;
			}

			_body_pos = (std::min)(_body_len, body_part);
			memcpy(_body, (const char *)data + head_part, _body_pos);
			return head_part + _body_pos;
		}
		else {
			memcpy(_head + _head_pos, data, len);
			_head_pos += len;
			return len;
		}
	}
	else {
		size_t commit_size = (std::min)(len, _body_len - _body_pos);
		memcpy(_body+_body_pos, data, commit_size);
		_body_pos += commit_size;
		return commit_size;
	}
#else
#endif
	return 0;
}

bool NetPacket::all_recv()
{
	return all_sent();
}

char * NetPacket::recv_buf()
{
	return (char *)send_buf();
}

size_t NetPacket::recv_buf_len()
{
#ifdef PACKET_MEMORY_PREALLOCATE
	return PACKET_MAX_SIZE - _head_pos - _body_pos;
#else
	return send_buf_len();
#endif
}

int NetPacket::commit_recv(size_t len)
{
#ifdef PACKET_MEMORY_PREALLOCATE
	if (_head_pos < PACKET_HEAD_LENGTH) {
		if (_head_pos + len >= PACKET_HEAD_LENGTH) {
			_body_pos += _head_pos + len - PACKET_HEAD_LENGTH;
			_head_pos = PACKET_HEAD_LENGTH;

			_body_len = SocketHelper::net_ntohs(_head);
			//std::cout << "message length: " << _body_len << "\n";
			if (_body_len > PACKET_MAX_SIZE - PACKET_HEAD_LENGTH) {
				std::cout << "invalid packet\n";
				return -1;
			}
		}
		else {
			_head_pos += len;
		}
	}
	else {
		_body_pos += len;
	}
#else
	if (_head_pos != PACKET_HEAD_LENGTH) {
		_head_pos += len;
		if (_head_pos == PACKET_HEAD_LENGTH) {
			_body_len = SocketHelper::net_ntohs(_head);
			if (_body_len > PACKET_MAX_SIZE - PACKET_HEAD_LENGTH) {
				std::cout << "invalid packet\n";
				return -1;
			}
			_body = (uint8_t *)malloc(_body_len);
			//std::cout << "message length: " << _body_len << "\n";
		}
	}
	else {
		_body_pos += len;
	}
#endif
	return 0;
}

bool NetPacket::all_sent()
{
	return (_head_pos == PACKET_HEAD_LENGTH) && (_body_len > 0) && (_body_pos == _body_len);
}

const char * NetPacket::send_buf()
{
#ifdef PACKET_MEMORY_PREALLOCATE
	return (const char *)(_head + _head_pos + _body_pos);
#else
	if (_head_pos != PACKET_HEAD_LENGTH) {
		return (const char *)(_head + _head_pos);
	}
	else {
		return (const char *)(_body + _body_pos);
	}
#endif
}

size_t NetPacket::send_buf_len()
{
#ifdef PACKET_MEMORY_PREALLOCATE
	return PACKET_HEAD_LENGTH + _body_len - _head_pos - _body_pos;
#else
	if (_head_pos != PACKET_HEAD_LENGTH) {
		return PACKET_HEAD_LENGTH - _head_pos;
	}
	else {
		return _body_len - _body_pos;
	}
#endif
}

void NetPacket::commit_send(size_t len)
{
#ifdef PACKET_MEMORY_PREALLOCATE
	if (_head_pos < PACKET_HEAD_LENGTH) {
		if (_head_pos + len >= PACKET_HEAD_LENGTH) {
			_body_pos += _head_pos + len - PACKET_HEAD_LENGTH;
			_head_pos = PACKET_HEAD_LENGTH;
		}
		else {
			_head_pos += len;
		}
		}
	else {
		_body_pos += len;
	}
#else
	if (_head_pos != PACKET_HEAD_LENGTH) {
		_head_pos += len;
	}
	else {
		_body_pos += len;
	}
#endif
}



}

